home *** CD-ROM | disk | FTP | other *** search
/ Collection of Tools & Utilities / Collection of Tools and Utilities.iso / sound / sbprog10.zip / SBDEVICE.CPP < prev    next >
C/C++ Source or Header  |  1993-09-15  |  11KB  |  352 lines

  1. // SbDevice.cpp
  2.  
  3. // Function definitions for the SbDevice class. Allows a program to make
  4. // use of the Soundblaster's DMA recording/playback modes.
  5.  
  6. // Written by Christopher M. Box (1993).
  7. // Some functions were based on Soundblaster Freedom Project code.
  8.  
  9. #include <conio.h>
  10. #include <process.h>
  11. #include <dos.h>
  12.  
  13. #include "sndclass.h"
  14.  
  15. // Define maximum sampling speeds for SB low-speed mode
  16. #define MAX_LO_PLAY 22222
  17. #define MAX_LO_REC 12048
  18. // Define maximum wait when writing a command to the SB
  19. #define CMD_TIMEOUT 500000UL
  20.  
  21. // Command array used to store SB commands
  22. static byte sb_cmd_data[5];
  23. static volatile byte sb_cmd_len;
  24.  
  25. // Constructor - calls ASM SB detection routine and initialises variables
  26.  
  27. SbDevice::SbDevice(void) {
  28.     Dprint(("Soundblaster initialisation.\r\n"));
  29.     if (dsp_reset()) {
  30.         cprintf("Soundblaster not found.\r\n");
  31.         exists = 0;
  32.     } else {
  33.         exists = 1;
  34.         init_irq();            // Install interrupt handler
  35.         sb_size = 0;
  36.     }
  37. }
  38.  
  39. // Destructor - switches off SB
  40.  
  41. SbDevice::~SbDevice(void) {
  42.     if (exists) {
  43.         Dprint(("Destruct sb device.\r\n"));
  44.         deinit_irq();
  45.         voice(0);          // Turn off voice output
  46.         dsp_reset();       // Reset SB
  47.     }
  48.     prevent_dma(SbDMAchan);
  49. }
  50.  
  51. // Function: set_rate
  52. // Set the sampling rate, subject to the SB's granular speed-setting ability.
  53. // Stores the resulting rate in the 'rate' variable (this is usually near
  54. // to, but not the same as 'new_rate'). Automatically enables high speed
  55. // mode if necessary, but it needs to know the intended data direction
  56. // ('dir') to do this.
  57.  
  58. void SbDevice::set_rate(unsigned new_rate, byte dir) {
  59.     byte tc;              // Time constant
  60.  
  61.     if (!exists) return;
  62.     tc = (byte) (256 - ((1000000L + new_rate/2)/new_rate));
  63.     rate = (unsigned) (1000000L / (256 - tc));
  64.     hi_speed = (rate > (dir == PLAY ? MAX_LO_PLAY : MAX_LO_REC));
  65.     Dprint(("Time constant %i. Hispeed %i.\r\n",(int)tc,hi_speed));
  66.     dsp_cmd(TIME_CONSTANT);         // Command byte for sample rate
  67.     dsp_cmd(tc);                    // Sample rate time constant
  68. }
  69.  
  70. // Function: dsp_cmd
  71. // Send a command byte ('cmd') to the SB, after waiting for the busy flag to
  72. // clear. If the SB locks up and never clears the busy flag, it prints
  73. // an error message and exits.
  74.  
  75. void SbDevice::dsp_cmd(byte cmd) {
  76.     unsigned long wait = 0;
  77.  
  78.     while (inportb(DSP_WRITE_STATUS) & 0x80) {
  79.         if (++wait > CMD_TIMEOUT) {
  80.             cprintf("Timeout while waiting to write command to SB.\r\n");
  81.             exit(1);
  82.         }
  83.     }
  84.     outportb(DSP_WRITE_DATA,cmd);
  85.     Dprint(("Waited %lu to write %x.\r\n",wait,(int)cmd));
  86. }
  87.  
  88. // Function: voice
  89. // Enables or disables the SB's voice output according to 'state'.
  90.  
  91. void SbDevice::voice(int state) {
  92.     dsp_cmd((state) ? SPEAKER_ON : SPEAKER_OFF);
  93. }
  94.  
  95. // Function: buf_dma_start
  96. // Starts buffered DMA to/from the SB. 'buffer' points to the start of
  97. // the buffer area, and 'buflen' holds the length of the buffer (both halves)
  98. // in bytes. 'dir' sets the direction.
  99.  
  100. void SbDevice::buf_dma_start(byte far *buffer, unsigned buflen, byte dir) {
  101.     byte im, tm;             // Interrupt masks
  102.  
  103.     if (!exists) {
  104.         cprintf("Fatal error: no SB exists.\r\n");
  105.         exit(-1);
  106.     }
  107.  
  108.     im = inportb(0x21);
  109.     tm = ~(1 << SbIRQ);
  110.     outportb(0x21,im & tm);      // Enable SB interrupt
  111.     enable();
  112.  
  113.     // First ensure that the channel is inactive before setting it up
  114.     if (prevent_dma(SbDMAchan)) {
  115.         cprintf("DMA: %s\r\n", dma_errlist[dma_errno]);
  116.         exit(1);
  117.     }
  118.  
  119.     // Next prepare the DMA controller
  120.     if (dma_setup(SbDMAchan,buffer,buflen-1,dir)) {
  121.         cprintf("DMA setup: %s\r\n", dma_errlist[dma_errno]);
  122.         exit(1);
  123.     }
  124.     direction = dir;
  125.  
  126.     // Setup Soundblaster for transfer
  127.     set_rate(rate, direction);
  128.     voice(dir == PLAY);
  129.     sb_size = 0;            // SB card forgets last buffer size so remind it
  130.     set_sb_cmds(buflen);    // Work out the commands to send
  131.     int i=0;
  132.     while (i < sb_cmd_len) dsp_cmd(sb_cmd_data[i++]);     // And send them
  133.  
  134.     Dprint(("Buffered DMA started - length %u\r\n",buflen));
  135. }
  136.  
  137. // Function: set_sb_cmds
  138. // Private function to work out the necessary commands to start an SB
  139. // transfer, and store these in an array. 'buflen' tells it the total
  140. // number of bytes to play/record.
  141.  
  142. #define STORE(x) sb_cmd_data[sb_cmd_len++] = (x)
  143.  
  144. void SbDevice::set_sb_cmds(unsigned buflen) {
  145.     sb_cmd_len = 0;
  146.     unsigned bl = buflen - 1;
  147.     if (hi_speed) {              // Different commands in hispeed mode
  148.         if (buflen != sb_size) {  // Only need to set the length if it hasn't
  149.             STORE(SET_HS_SIZE);   // been used before
  150.             STORE(bl & 0xff);
  151.             STORE(bl >> 8);
  152.         }
  153.         STORE((direction == PLAY) ? HS_DAC : HS_ADC);
  154.     } else {
  155.         STORE((direction == PLAY) ? DMA_8_BIT_DAC : DMA_ADC);
  156.         STORE(bl & 0xff);       // Always set the length in low-speed mode
  157.         STORE(bl >> 8);
  158.     }
  159.     sb_size = buflen;
  160.     Dprint(("%i commands stored.\r\n", (int)sb_cmd_len));
  161. }
  162.  
  163. // Function: process_keys
  164. // Called by buf_dma_lo and buf_dma_hi whenever a key has been pressed
  165. // (kbhit() is true) while waiting for the DMA to reach the end
  166. // of a buffer. It defines the actions to be taken, depending on the key.
  167. // Returns 0 if the calling function should continue, otherwise it returns
  168. // the character that was pressed.
  169.  
  170. int SbDevice::process_keys(void) {
  171.     int c;
  172.     c = getch();
  173.     if (c == 'p') {          // Key 'p' means pause
  174.         halt();
  175.         getch();
  176.         cont();
  177.     } else {                 // Anything else stops the SB DMA
  178.         if (dsp_reset()) cprintf("Bad dsp reset.");
  179.         Dprint(("Terminated.\r\n"));
  180.         return(c);
  181.     }
  182.     return 0;
  183. }
  184.  
  185. // Function: buf_dma_lo
  186. // Called when the foreground routine has finished its task and wishes
  187. // to wait for the DMA to complete the low half-buffer. It checks for overrun
  188. // (DMA already into the high buffer) and keyboard activity. It returns zero
  189. // when the DMA has completed, and non-zero if a key has been pressed. The
  190. // single argument, 'len', defines the length of the low buffer.
  191.  
  192. int SbDevice::buf_dma_lo(unsigned len) {
  193.     if (len > sb_size) {
  194.         cprintf("Bad length.");
  195.         exit(1);
  196.     }
  197.     register unsigned ad = dma_addr();
  198.     Dprint(("Lo addr = %X. ",ad));
  199.     // Current address needs to be between 0 and len, otherwise
  200.     // something has gone wrong.
  201.     if (ad > len) {
  202.         Dprint(("Overrun - skipping buffer."));
  203.         while ((ad = dma_addr()) > len);            // Skip high buffer
  204.     }
  205.     Dprint(("\r\n"));
  206.     // If ad==0 then either the CPU is very fast, or the transfer has already
  207.     // finished and auto-initialised. We assume the second case. The first
  208.     // only occurs at the start, and is dealt with in file_dma().
  209.     int c;
  210.     while ((ad=dma_addr()) < len && ad) {
  211.         // Terminate waiting loop if either:
  212.         // 1. DMA current address reaches 'len' or more
  213.         // 2. Current address is zero (after an auto-initialise)
  214.         // Meanwhile, check for keypresses
  215.         if (kbhit()) {
  216.             if ((c=process_keys()) != 0) return(c);
  217.         }
  218.     }
  219.     // Now set up variables for high buffer.
  220.     lo_buf_sz = len;
  221.     if (sb_size == len) {
  222.         Dprint(("Finished buffered DMA.\r\n"));
  223.     }
  224.     return 0;
  225. }
  226.  
  227. // Function: buf_dma_hi
  228. // This is the corresponding high-buffer function. An extra argument is
  229. // required ('next_buflen') which defines the size of the buffer length to
  230. // be used after the high buffer has completed. This is usually the same
  231. // as the current length, or perhaps zero.
  232.  
  233. int SbDevice::buf_dma_hi(unsigned len, unsigned next_buflen) {
  234.     len += lo_buf_sz;
  235.     if (len != sb_size) {
  236.         cprintf("Bad length.");
  237.         exit(1);
  238.     }
  239.     register unsigned ad = dma_addr();
  240.     Dprint(("Hi addr = %X. ",ad));
  241.     // Address needs to be between buf_size and len
  242.     if (ad < lo_buf_sz) {
  243.         Dprint(("Overrun - skipping buffer. "));
  244.         while ((ad = dma_addr()) < lo_buf_sz);    // Skip the low buffer
  245.     }
  246.     Dprint(("\r\n"));
  247.     int c;
  248.     if (next_buflen) {
  249.         // There's another DMA after this so set up an interrupt routine.
  250.         // dma count does not change (assumes buffer len will not get longer)
  251.         set_sb_cmds(next_buflen);
  252.         setvect(SbIRQ+8, sb_buf_dma_int);    // Select the interrupt handler
  253.         while(sb_cmd_len) {        // Wait for end-of-dma interrupt
  254.             if (kbhit()) {      // (which will set sb_cmd_len